/*
 * Copyright (c) 2011-2012 Alexander Dubu
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * o  Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * o  Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * o  Neither the name Axil nor the names of its contributors may be used to
 *    endorse or promote products derived from this software without specific
 *    prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package axil.compiler.ast;

import axil.api.extend.Function;
import axil.api.extend.Operator;
import axil.compiler.Compilation;
import axil.compiler.Scope;
import axil.compiler.api.Verifier;
import axil.definition.OperatorDefinition;
import axil.framework.error.CompilationException;
import axil.framework.localization.Message;
import axil.vm.opcode.OpCallLazy;
import axil.vm.opcode.OpOperator;
import axil.vm.opcode.Opcode;

import static axil.framework.Functions.debug;
import static axil.framework.Functions.list;


/**
 * An AST node defining an operator. All operators in Axil are binary, with a
 * mandatory LHS and RHS operands.
 */
public class OperatorNode extends AbstractNode {
	private static final int[] ids = new int[] {0, 0};

	private Node left;
	private Node right;
	private OperatorDefinition op;


	public OperatorNode(Node left, OperatorDefinition op, Node right) {
		this.left = left;
		this.op = op;
		this.right = right;
	}


	/**
	 * Compile this node, verifying correctness and performing any optimizations
	 * possible. The compilation context and scope given are never null but may
	 * not contain the desired functions and identifiers.
	 */
	public Node compile(Compilation context, Scope scope) {
		left = left.compile(context, scope);
		right = right.compile(context, scope);
		Verifier v = null;
		if (op.lazy()) {
			Function f = op.function();
			v = f instanceof Verifier ? (Verifier)f : null;
		} else {
			Operator o = op.operator();
			v = o instanceof Verifier ? (Verifier)o : null;
		}
		if (v != null) {
			/*
			 * If the operator has special verification abilities, we activate
			 * these now. If a message is returned, it is an indication of a
			 * problem.
			 */
			Message msg = v.verify(list(left, right));
			if (msg != null) {
				throw new CompilationException(this, msg);
			}
		}
		return this;
	}


	/**
	 * Provide a brief description of what this node is. This should include the
	 * kind of thing it is, not the contents of the thing.
	 */
	public String what() {
		return "operator";
	}


	/**
	 * Dump the content of this node to the diagnostic device. This is strictly
	 * for internal diagnostics, not for application use.
	 */
	public void dump(int indent) {
		debug(indent, what());
		debug(indent + 1, "LHS:");
		left.dump(indent + 2);
		debug(indent + 1, "RHS:");
		right.dump(indent + 2);
	}


	/**
	 * Emit VM instructions that represents an executable version of this AST
	 * node. The instruction returned cannot be null. An exception is thrown if
	 * any latent defects in the AST are discovered.
	 */
	public Opcode emit() {
		if (op.lazy()) {
			return new OpCallLazy(this, op.function(), ids,
			                      left.emit(), right.emit());
		}
		return new OpOperator(this, left.emit(), op.operator(), right.emit());
	}
}
